using TerritoryGame.Application.Interfaces;
using TerritoryGame.Domain.Entities.App;
using TerritoryGame.Infrastructure.Data;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using StackExchange.Redis;
using TerritoryGame.Application.Dtos;

namespace TerritoryGame.Application.Services;

/// <summary>
/// 游戏缓存服务实现
/// </summary>
public class GameCacheService : IGameCacheService
{
    private readonly IRedisService _redisService;
    private readonly ILogger<GameCacheService> _logger;
    
    // Redis键前缀
    private const string ROOM_PREFIX = "game:room:";
    private const string PLAYER_PREFIX = "game:player:";
    private const string PAINT_PREFIX = "game:paint:";
    private const string SCORE_PREFIX = "game:score:";
    private const string STATUS_PREFIX = "game:status:";
    private const string ONLINE_ROOMS_KEY = "game:online:rooms";

    public GameCacheService(IRedisService redisService, ILogger<GameCacheService> logger)
    {
        _redisService = redisService;
        _logger = logger;
    }

    public async Task<bool> CacheGameRoomAsync(GameRoom room)
    {
        try
        {
            var key = $"{ROOM_PREFIX}{room.Id}";
            var expiry = TimeSpan.FromMinutes(30); // 房间缓存30分钟，更合理的游戏房间生命周期
            return await _redisService.SetAsync(key, room, expiry);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "缓存游戏房间失败: {RoomId}", room.Id);
            return false;
        }
    }

    public async Task<GameRoom?> GetGameRoomAsync(string roomId)
    {
        try
        {
            var key = $"{ROOM_PREFIX}{roomId}";
            return await _redisService.GetAsync<GameRoom>(key);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "获取游戏房间缓存失败: {RoomId}", roomId);
            return null;
        }
    }

    public async Task<bool> RemoveGameRoomAsync(string roomId)
    {
        try
        {
            var key = $"{ROOM_PREFIX}{roomId}";
            return await _redisService.DeleteAsync(key);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "删除游戏房间缓存失败: {RoomId}", roomId);
            return false;
        }
    }

    public async Task<bool> CachePlayerAsync(string roomId, Player player)
    {
        try
        {
            var key = $"{PLAYER_PREFIX}{roomId}:{player.Id}";
            var expiry = TimeSpan.FromMinutes(30); // 与房间过期时间保持一致
            return await _redisService.SetAsync(key, player, expiry);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "缓存玩家失败: {RoomId}, {PlayerId}", roomId, player.Id);
            return false;
        }
    }

    public async Task<List<Player>> GetRoomPlayersAsync(string roomId)
    {
        try
        {
            var db = _redisService.GetDatabase();
            var pattern = $"{PLAYER_PREFIX}{roomId}:*";
            var keys = db.Multiplexer.GetServer(db.Multiplexer.GetEndPoints().First()).Keys(pattern: pattern);
            
            var players = new List<Player>();
            foreach (var key in keys)
            {
                var player = await _redisService.GetAsync<Player>(key);
                if (player != null)
                {
                    players.Add(player);
                }
            }
            
            return players;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "获取房间玩家列表失败: {RoomId}", roomId);
            return new List<Player>();
        }
    }

    public async Task<bool> RemovePlayerAsync(string roomId, Guid playerId)
    {
        try
        {
            var key = $"{PLAYER_PREFIX}{roomId}:{playerId}";
            return await _redisService.DeleteAsync(key);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "移除玩家缓存失败: {RoomId}, {PlayerId}", roomId, playerId);
            return false;
        }
    }

    public async Task<bool> CachePaintActionAsync(string roomId, PaintAction action)
    {
        try
        {
            var key = $"{PAINT_PREFIX}{roomId}:{action.Id}";
            var expiry = TimeSpan.FromHours(1); // 涂色动作缓存1小时
            return await _redisService.SetAsync(key, action, expiry);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "缓存涂色动作失败: {RoomId}, {ActionId}", roomId, action.Id);
            return false;
        }
    }

    public async Task<List<PaintAction>> GetPaintActionsAsync(string roomId, int limit = 100)
    {
        try
        {
            var db = _redisService.GetDatabase();
            var pattern = $"{PAINT_PREFIX}{roomId}:*";
            var keys = db.Multiplexer.GetServer(db.Multiplexer.GetEndPoints().First()).Keys(pattern: pattern);
            
            var actions = new List<PaintAction>();
            foreach (var key in keys.Take(limit))
            {
                var action = await _redisService.GetAsync<PaintAction>(key);
                if (action != null)
                {
                    actions.Add(action);
                }
            }
            
            return actions.OrderBy(a => a.CreatedAt).ToList();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "获取涂色动作历史失败: {RoomId}", roomId);
            return new List<PaintAction>();
        }
    }

    public async Task<bool> UpdatePlayerScoreAsync(string roomId, Guid playerId, double score)
    {
        try
        {
            var key = $"{SCORE_PREFIX}{roomId}";
            var db = _redisService.GetDatabase();
            return await db.SortedSetAddAsync(key, playerId.ToString(), score);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "更新玩家分数失败: {RoomId}, {PlayerId}, {Score}", roomId, playerId, score);
            return false;
        }
    }

    public async Task<List<RoomLeaderboardEntry>> GetRoomLeaderboardAsync(string roomId)
    {
        try
        {
            var key = $"{SCORE_PREFIX}{roomId}";
            var db = _redisService.GetDatabase();
            var entries = await db.SortedSetRangeByScoreWithScoresAsync(key, order: Order.Descending);
            
            var leaderboard = new List<RoomLeaderboardEntry>();
            foreach (var entry in entries)
            {
                var playerId = Guid.Parse(entry.Element);
                var score = entry.Score;
                var rank = await db.SortedSetRankAsync(key, entry.Element) ?? -1;
                
                leaderboard.Add(new RoomLeaderboardEntry
                {
                    PlayerId = playerId,
                    Score = score,
                    Rank = (int)rank + 1
                });
            }
            
            return leaderboard;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "获取房间排行榜失败: {RoomId}", roomId);
            return new List<RoomLeaderboardEntry>();
        }
    }

    public async Task<bool> SetRoomStatusAsync(string roomId, string status)
    {
        try
        {
            var key = $"{STATUS_PREFIX}{roomId}";
            var expiry = TimeSpan.FromHours(2);
            return await _redisService.SetAsync(key, status, expiry);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "设置房间状态失败: {RoomId}, {Status}", roomId, status);
            return false;
        }
    }

    public async Task<string?> GetRoomStatusAsync(string roomId)
    {
        try
        {
            var key = $"{STATUS_PREFIX}{roomId}";
            return await _redisService.GetAsync<string>(key);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "获取房间状态失败: {RoomId}", roomId);
            return null;
        }
    }

    public async Task<bool> SetRoomExpiryAsync(string roomId, TimeSpan expiry)
    {
        try
        {
            var db = _redisService.GetDatabase();
            var roomKey = $"{ROOM_PREFIX}{roomId}";
            var playerPattern = $"{PLAYER_PREFIX}{roomId}:*";
            var paintPattern = $"{PAINT_PREFIX}{roomId}:*";
            var scoreKey = $"{SCORE_PREFIX}{roomId}";
            var statusKey = $"{STATUS_PREFIX}{roomId}";
            
            // 设置所有相关键的过期时间
            await db.KeyExpireAsync(roomKey, expiry);
            await db.KeyExpireAsync(scoreKey, expiry);
            await db.KeyExpireAsync(statusKey, expiry);
            
            // 设置玩家和涂色动作的过期时间
            var keys = db.Multiplexer.GetServer(db.Multiplexer.GetEndPoints().First()).Keys(pattern: playerPattern);
            foreach (var key in keys)
            {
                await db.KeyExpireAsync(key, expiry);
            }
            
            keys = db.Multiplexer.GetServer(db.Multiplexer.GetEndPoints().First()).Keys(pattern: paintPattern);
            foreach (var key in keys)
            {
                await db.KeyExpireAsync(key, expiry);
            }
            
            return true;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "设置房间过期时间失败: {RoomId}", roomId);
            return false;
        }
    }

    public async Task<List<string>> GetOnlineRoomsAsync()
    {
        try
        {
            var rooms = await _redisService.GetAsync<List<string>>(ONLINE_ROOMS_KEY);
            return rooms ?? new List<string>();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "获取在线房间列表失败");
            return new List<string>();
        }
    }

    public async Task<bool> AddOnlineRoomAsync(string roomId)
    {
        try
        {
            var rooms = await GetOnlineRoomsAsync();
            if (!rooms.Contains(roomId))
            {
                rooms.Add(roomId);
                return await _redisService.SetAsync(ONLINE_ROOMS_KEY, rooms, TimeSpan.FromHours(24));
            }
            return true;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "添加在线房间失败: {RoomId}", roomId);
            return false;
        }
    }

    public async Task<bool> RemoveOnlineRoomAsync(string roomId)
    {
        try
        {
            var rooms = await GetOnlineRoomsAsync();
            if (rooms.Contains(roomId))
            {
                rooms.Remove(roomId);
                return await _redisService.SetAsync(ONLINE_ROOMS_KEY, rooms, TimeSpan.FromHours(24));
            }
            return true;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "移除在线房间失败: {RoomId}", roomId);
            return false;
        }
    }
}
